home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-04-03 | 13.7 KB | 321 lines | [TEXT/MPS ] |
- // UBusyCursor.h
- // Copyright © 1984-96 by Apple Computer, Inc. All rights reserved.
-
- #ifndef __UBUSYCURSOR__
- #define __UBUSYCURSOR__
-
- //----------------------------------------------------------------------------------------
- // THEORY OF OPERATION
- //
- // The UBusyCursor unit implements a mechanism for automatically setting the cursor to the
- // busy cursor when the application is "busy." An application is considered "busy" if it
- // hasn't called GetNextEvent or EventAvail for a given number of ticks, where 60 ticks
- // equals one second.The default time is defined by kBusyDelay, which is 120 ticks or 2
- // seconds--it can be changed by calling BusyDelay. After this period of time has elapsed
- // with no call to GetNextEvent or EventAvail, the cursor is changed to the busy cursor.On
- // the next call to GetNextEvent or EventAvail, the cursor is restored to its state before
- // it was changed to the busy cursor.
- //
- // Changing the cursor to the busy cursor is done by the VBL task ABusyTask. ABusyTask's
- // execution frequency is set to kBusyDelay, or by calling BusyDelay. When ABusyTask is
- // executed it sets the cursor to the busy cursor and resets ABusyTask's vblCount. The
- // traps InitCursor, SetCursor and SetCCursor are patched so that they "remember" the
- // cursor being set before executing the trap.This will allow us to restore the cursor
- // after it has been changed to the busy cursor.
- //
- // The EventAvail and GetNextEvent traps are patched such that, before executing the trap,
- // ABusyTask's vblCount is reset, and if the busy cursor is displayed, then the cursor is
- // restored to the last cursor set by SetCursor or SetCCursor.
- //
- // Manual vs. Automatic Animation
- //
- // Now, there are two philosophies as to how to make a busy cursor animate: automatic and
- // manual. The automatic technique requires no intervention by the application, and is
- // essentially an extension of the current MacApp busy cursor philosophy. One way of
- // implementing automated animation is by installing a VBL task which changes the cursor
- // at appropriate time intervals, without the applications knowledge. The advantage of
- // this approach, of course, is that you dont have to do anything in your application to
- // make it work, plus you get a cursor that animates in a smooth manner. The drawback is
- // the possibility of a run away cursor. By using a VBL task the watch may spin even
- // though the application has locked up, say in an endless loop, and isnt actually doing
- // anything. This is because the VBL task has no idea what the application is doing. This
- // happened to me (Curt Bianchi) once with a well-known word processor. I tried to save a
- // file and the word processor locked up for hours, but the watch kept spinning.
- // Eventually I gave up and pressed the program interrupt switch on the side of the
- // computer. Since I didnt have a debugger installed, the bomb alert came up, and all the
- // while the hands kept spinning, oblivious to what was happening!
- //
- // With the manual technique the application tells the cursor when to animate. This is
- // done by sprinkling cursor animation calls throughout your code, which can be a real
- // pain. You also wind up with jumpy or irregular animation. However, it does prevent the
- // run away cursor problem. Both the Finder (6.0) and MPW (3.2) use this technique.
- //
- // It turns out that theres a simple way to combine the advantages of both approaches,
- // which Ill use here. That is, you can get smooth animation and avoid the run away cursor
- // problem. Its done by animating the cursor as long as the application lets us know its
- // still working. If the application fails to check in, then the animation times out. You
- // can choose between automatic and manual animation by setting the length of time before
- // time out occurs. A very large time out value, say LONG_MAX which for our purposes is
- // infinity, means the application never has to check in and we get automatic cursor
- // animation. A small time out value, say a few minutes, forces the application to check
- // in from time to time, or the cursor quits animating after the period of time has
- // elapsed.
- //
- // To implement this scheme, we need a way for the application to check in, and a way for
- // the application to set the period of time after which the animation times out. To set
- // the period of time well add the routine SetBusyTimeout, which accepts a value measured
- // in ticks, or sixtieths (1/ 60) of a second. For example, five minutes is 18000 ticks: 5
- // * 60 * 60 = 18000. Well also implement the routine BusyAnimate, which the application
- // calls to notify us that its still working. Provided BusyAnimate is called more often
- // than the time out value, the cursor continues to animate smoothly. So, if you pass
- // 18000 (5 minutes) to SetBusyTimeout, the busy cursor will continue to animate as long
- // as BusyAnimate is called at least every five minutes, or until the application attempts
- // to handle another event.
- //----------------------------------------------------------------------------------------
-
- // MacApp
-
- #ifndef __UOBJECT__
- #include "UObject.h"
- #endif
-
- // Toolbox
-
- #ifndef __QUICKDRAW__
- #include <Quickdraw.h>
- #endif
-
- #ifndef __RETRACE__
- #include <Retrace.h>
- #endif
-
- // ANSI
-
- #ifndef __LIMITS__
- #include <limits.h>
- #endif
-
-
- //----------------------------------------------------------------------------------------
- // Constants
- //----------------------------------------------------------------------------------------
-
- const short kBusyDelay = 2 * 60; // default # of 1/ 60 sec. ticks before
- // cursor changes to a busy cursor
-
- const short kAnimateDelay = 10; // Animate every 1/ 6 of a second.
-
- const long kTicksBeforeTimeout = LONG_MAX; // Default is to never time out
-
-
- //----------------------------------------------------------------------------------------
- // These types are used to represent the acur resources. The 'cursors' field will actually
- // index into a variable number of cursors. We need one as a place holder.
- //----------------------------------------------------------------------------------------
-
- #if PRAGMA_ALIGN_SUPPORTED
- #pragma options align=mac68k
- #endif
- struct AcurRsrcRecord
- {
- short noOfCursors;
- ResNumber whichCursor;
-
- union
- {
- struct
- {
- ResNumber rsrcId;
- short filler;
- } Disk;
- // The cursor resource is still on disk, and needs to be read in.
-
- struct
- {
- CursHandle h;
- } Memory;
- // The cursor resource is currently in memory.
-
- } cursors[1];
- };
- #if PRAGMA_ALIGN_SUPPORTED
- #pragma options align=reset
- #endif
-
- typedef AcurRsrcRecord* AcurRsrcPtr;
- typedef AcurRsrcPtr* AcurRsrcHandle;
-
- class TBusyCursor;
-
- //----------------------------------------------------------------------------------------
-
- struct QElemWithBusyCursor
- {
- VBLTask q; // vbl queue element for changing the cursor
- TBusyCursor* fBusyCursor;
-
- QElemWithBusyCursor();
- };
-
- inline QElemWithBusyCursor::QElemWithBusyCursor()
- {
- fBusyCursor = NULL;
- }
-
- //----------------------------------------------------------------------------------------
- // TBusyCursor
- //----------------------------------------------------------------------------------------
-
- class TBusyCursor : public TObject
- {
- MA_DECLARE_CLASS;
-
- public:
-
- TBusyCursor();
- // Empty constructor to satisfy compiler.
-
- void IBusyCursor();
- // IBusyCursor installs the busy cursor mechanism. Typically this is called once
- // during program initialization. It installs the VBL task and patches the traps.
-
- virtual ~TBusyCursor();
- // Free uninstalls the busy cursor mechanism. Typically this is called once during
- // program termination.It removes the VBL task and unpatches the patched traps.
-
- virtual AcurRsrcHandle InitializeAnimatedCursor(ResNumber acurRsrcId);
- // Loads the acur resource whose id is acurRsrcId, and then loads the CURS
- // resources referred to by the acur resource.All of these resources must be
- // locked in memory since they are accessed at VBL interrupt time.We also detach
- // them from the resource map to prevent the Resource Mgr from unlocking them out
- // from under us.
-
- virtual Boolean Activate(Boolean entering);
- // Call BusyActivate if you want to activate or deactivate the busy cursor
- // mechanism. This is call by MacApp when losing/ gaining control to a desk
- // accessory or another application.
-
- virtual Boolean InControl(Boolean entering, Boolean reset);
- // Allows shutting off control, checking events via ENE/GNE/WNE and
- // restoring control all without disturbing (resetting) a busy cursor already
- // being shown.
-
- virtual void SetDelay(short newDelay);
- // Call SetDelay if you want to change the busy cursor delay. newDelay should be in
- // 1/60 seconds; a value <= 0 means don't change the (SetDelay respects the state flags
- // in the cursor info record (ie., changeToBusy and inControl.)
-
- virtual void ForceBusy();
- // Puts up the busy cursor immediately.
-
- virtual void Reset(short delayTicks);
- // Call Reset if you want to change the cursor back to an arrow and reset the time
- // before changing back to a busy cursor.This is not usually called directly by
- // the application.Instead, it is called each time GetNextEvent and EventAvail is
- // called, by patches installed by IBusyCursor.
-
- virtual void TurnOff();
- // This is called from InitMacAppCursor, SetMacAppCursor and SetCMacAppCursor. It
- // sets pCursorInfo fields to indicate that the cursor is not the busy cursor.
-
- virtual void Animate();
- // call this to prevent the animation from timing out.
-
- virtual void SetTimeout(long ticksBeforeTimeout);
- // call this to set the length of time before animation times out.
-
- virtual Boolean KeepCursorBusy(Boolean keepItSpinning);
- // set this if you want to call any of the patched traps without resetting the
- // cursor
-
- virtual void SetAnimatedCursor(ResNumber itsACUR, short animateDelay);
- // Call this to set the cursor to a new animation sequence
-
- virtual void ReleaseAnimatedCursor();
- // Call this to release (dispose) the current animation sequence
-
- virtual void SetMacAppCursor(const Cursor* theCursor);
- // Remembers the cursor being set
-
- virtual void SetCMacAppCursor(CCrsrHandle theCCursor);
- // Remembers the cursor being set
-
- virtual void BusyTask();
- // Called from VBL to perform the busy task
-
- //----------------------------------------------------------------------------------------
- // static member functions
- //----------------------------------------------------------------------------------------
- public:
-
- static void InitUBusyCursor();
- // Inits the unit. Creates the global gBusyCursor.
-
- static void TerminateUBusyCursor();
- // Terminates the unit
-
- static CursHandle NextAnimatedCursor(AcurRsrcHandle acurRsrc);
- // This routine will return the next cursor from the animation list.
-
- static void ResetBusyCursor();
-
- protected:
- static pascal void SetCMacAppCursorPatch(CCrsrHandle theCCursor);
- static pascal void SetMacAppCursorPatch(const Cursor* theCursor);
- static pascal void InitMacAppCursor();
-
- //----------------------------------------------------------------------------------------
- // data members
- //----------------------------------------------------------------------------------------
- public:
- QElemWithBusyCursor fQElemWithBusyCursor; // vbl queue elem. for changing the cursor
- Cursor fOrigCursor; // Cursor at the time the busy cursor was
- // put up, if not in color
- Cursor fCurrentCursor; // the current display cursor
- AcurRsrcHandle fCursorState; // the acur resource
- CCrsrHandle fOrigCCursor; // Cursor at the time the busy cursor was
- // put up, if in color
- long fTimeoutCount; // spins before animation times out
- long fSpinCount; // spins since last BusyAnimate call
-
- long fA5; // The value of A5 will be stored here to
- // be available at VBL time
- short fBusyDelay; // time in 1/ 60 second before cursor
- // changes to watch
- short fAnimateDelay; // ticks before we spin the cursor
- Boolean fInColor; // Is the saved cursor in color?
- Boolean fInControl; // managed by MacApp; true iff MacApp is
- // in control(); if false we don't change
- // the cursor at all
- Boolean fChangeToBusy; // if true, we automagically switch to the
- // busy cursor in the VBL task and switch
- // to fOrigCursor on a call to GetNextEvent
- // or EventAvail(); applications can
- // changed this as necessary
- Boolean fIgnoreReset; // cursor has been actively put into a
- // state that will ignore resets.
- Boolean fBusyOn; // true if the busy cursor on
-
- //----------------------------------------------------------------------------------------
- // static data members
- //----------------------------------------------------------------------------------------
- public:
-
- static TBusyCursor* fgBusyCursor; // THE busycursor handler.
-
- protected:
- static Boolean pBusyCursorVBLInstalled; // used for proper failure handling
- };
-
-
- //----------------------------------------------------------------------------------------
- // External declarations for global variables and funcitons.
- //----------------------------------------------------------------------------------------
- typedef TBusyCursor *TBusyCursorPtr;
-
- extern TBusyCursorPtr& gBusyCursor;
- // We'll leave this a reference which is initialized to refer to the static member
- // for compatibility.
-
- #endif
-